/*
 * Gear - A refined core library for writing reliable asynchronous applications with D programming language.
 *
 * Copyright (C) 2021 Kerisy.com
 *
 * Website: https://www.kerisy.com
 *
 * Licensed under the Apache-2.0 License.
 *
 */

module fasthttpd.HttpServer;

import gear.buffer.Bytes;

import gear.codec;

import gear.event;
import gear.logging.ConsoleLogger;

import gear.net.TcpListener;
import gear.net.TcpStream;

// for gear http
import gear.codec.Framed;
import fasthttpd.codec.HttpCodec;

public import fasthttpd.HttpContext;
public import fasthttpd.HttpRequest;
public import fasthttpd.HttpResponse;
public import fasthttpd.HttpStatusCode;
public import fasthttpd.HttpContext;

import fasthttpd.HttpRequestHandler;
import fasthttpd.Router;

class HttpServer
{
    private
    {
        TcpListener _listener;
        EventLoop _loop;
        Router!HttpRequestHandler _router;
    }

    this()
    {
        _loop = new EventLoop();
        _listener = new TcpListener(_loop);
        _router = new Router!HttpRequestHandler;
    }

    HttpServer Get(string route, HttpRequestHandler handler)
    {
        _router.add(route, HttpMethod.GET, handler);
        return this;
    }

    HttpServer Post(string route, HttpRequestHandler handler)
    {
        _router.add(route, HttpMethod.POST, handler);
        return this;
    }

    HttpServer Put(string route, HttpRequestHandler handler)
    {
        _router.add(route, HttpMethod.PUT, handler);
        return this;
    }

    HttpServer Delete(string route, HttpRequestHandler handler)
    {
        _router.add(route, HttpMethod.DELETE, handler);
        return this;
    }

    private void Handle(HttpContext httpContext)
    {
        auto handler = _router.match(httpContext.request().path(), httpContext.request().method(), httpContext.request().parameters);

        if (handler is null)
        {
            httpContext.send(httpContext.response().set_status(HttpStatusCode.NOT_FOUND).set_body("404 Not Found.").to_buffer());
        }
        else
        {
            handler(httpContext);
        }

        httpContext.end();
    }

    HttpServer Listen(ushort port)
    {
        _listener.Bind(port);
        _listener.Accepted((TcpListener sender, TcpStream connection)
            {
                Framed!(HttpRequest) framed = new Framed!(HttpRequest)(connection, new HttpCodec());

                framed.OnFrame((HttpRequest request)
                    {
                        HttpContext ctx = new HttpContext(connection);
                        ctx.request(request);
                        Handle(ctx);
                    });

                connection.Disconnected(() {
                        Infof("client disconnected: %s", connection.RemoteAddress.toString());
                    }).Closed(() {
                        Infof("connection closed, local: %s, remote: %s", connection.LocalAddress.toString(), connection.RemoteAddress.toString());
                    }).Error((IoError error) { 
                        Errorf("Error occurred: %d  %s", error.errorCode, error.errorMsg); 
                    });
            });

        return this;
    }

    void Start()
    {
        Infof("Listening on: %s", _listener.BindingAddress.toString());

        _listener.Start();
        _loop.Run();
    }
}
